<?php

define('BASEPATH',true);
define('FCPATH',dirname(__FILE__));


$include_file = array(
FCPATH . '/lifebalanz/config/config.php',
FCPATH . '/lifebalanz/config/database.php',
FCPATH .  '/lifebalanz/helpers/common_helper.php',
FCPATH .  '/lifebalanz/libraries/db_postgresql.php',
);

foreach($include_file as $v){
	require_once $v;
}

C('config',$config);
C('db_config',$db);


// worker
$worker = new GearmanWorker(); 
$worker->addServer();
// QQ upload function
$worker->addFunction("health_bg_upload", "health_handle");
// user upload data file , handle function
$worker->addFunction("data_bg_handle","data_handle");
while ($worker->work());


function health_handle($job)
{	
	$load = $job->workload();
	$handle = new Health_handle();

	if($load == 'auto'){
		// handle automatically
		$handle->do_upload_data_auto();
	}elseif(isset(unserialize($load )['token']) && unserialize($load )['token']){
		$handle->do_upload_by_token(unserialize($load )['token']);	
	}else{
		$params  = unserialize($load );
		$handle->upload($params);
	}

}

function data_handle($job){
	$info = $job->workload();
	$handle = new Data_handle();
	if($info == 'auto'){
		$handle->run($info);
	}else{
		$handle->run(unserialize($info));
	}
	
	
}


function db_instanse(){
	$db = C('db_config');
	$host = $db['default']['hostname'];	
	$dbuser = $db['default']['username'];	
	$dbpassword = $db['default']['password'];	
	$dbname =$db['default']['database'];	
	return new DB_PG($host,$dbname,$dbuser,$dbpassword);
}


/***************************************************************************************
 * qq health handle class
/***************************************************************************************/
class Health_handle{

	private $appid;
	private $openid;
	private $access_token;
	public $lastupdate;
	public $user;
	public $type;
	private $data;
	private $date;	// current handle date
	public $error;

	private $db;
	private  static $config;
	private static $db_config;

	public function __construct(){
		// db instanse
		$this->db = db_instanse();
	}
	// take upload action
	public  function upload($params){
		// var_dump(123);return;
		// return;
		$this->user = $params['user'];
		$this->type = $params['type'];
		$this->data = $params['data'];
		$this->_formate_data();
		if($this->data){
			foreach($this->data as $v){
				switch ($this->type) {
				    case 'steps':
				        // 1. report_steps upload
				        // handle one day data in background
				        $this->_handle_report_steps_by_user($this->user,$v['data']);
				        break;
				    case 'sleep':
				         // 2. maybe report_running...
				         $this->_handle_report_sleep_by_user($this->user,$v['data']);
				        break;
				    
				    default:
				        # code...
				        break;
				}

				// after one day  handle
				//  1 success: delete memcache data, update lastupdatemark*
				//  2 faild , save to redo list
				if(!$this->_after_upload_handle()) return false;

			}
		}

		unset($this->data);

		// rehandle faild datas store in memcache
		$this->_redo_upload();

	}

	/**
	 * [_do_upload_by_auto description]
	 * @return [type] [description]
	 */
	public function do_upload_data_auto(){
		$actions = array_keys(C('config')['health_type']);
		foreach($actions as $type){
			// order
			$mark = $this->_mark($type);
			// status
			$status = $this->_status($type);

			// user handle limit
			$limit = C('config')['qq_upload_handle_user_num']  ?C('config')['qq_upload_handle_user_num'] : 10;

			$users = $this->db->getArray("select * from user_qqhealth_api where is_join=1 and ".$status."=1 order by ".$mark." limit ".$limit);

			if($users){
				foreach($users as $user){
					// handle one user here
					$data = $this->_get_one_user_data($type,$user);
					if($data && is_array($data)){
						$gmclient= new GearmanClient();
						$gmclient->addServer();
						$params = serialize(array('user'=>$user,'data'=>$data,'type'=>$type));
						$gmclient->doBackground("health_bg_upload", $params);	
					}

					unset($data);
				}
			}
			unset($users);

		}
	}

	public function do_upload_by_token($token){
		if(!$token ) return;
		$this->_check_is_join($token);

		$actions = array_keys(C('config')['health_type']);
		foreach($actions as $type){
			// order
			$mark = $this->_mark($type);
			// status
			$status = $this->_status($type);

			// handle one user here
			$data = $this->_get_one_user_data($type,$this->user);
			if($data && is_array($data)){
				$gmclient= new GearmanClient();
				$gmclient->addServer();
				$params = serialize(array('user'=>$this->user,'data'=>$data,'type'=>$type));
				$gmclient->doBackground("health_bg_upload", $params);	
			}

		}

	}
	private function _check_is_join($token){
		//check token 
		$rs = $this->db->getRow("select * from user_token where token='{$token}'");
		// var_dump($rs);die;
		if(!$rs) {
			echo "invalid token";
			return false;	
		}
		// check is_join
		$is_join = $this->db->getRow("select * from user_qqhealth_api where tid=".$rs->tid);

		if(!$is_join || $is_join->is_join != 1) return "not join";

		$this->user = object2array($is_join);
	}

	private function _get_one_user_data($type,$user){
		// order
		$mark = $this->_mark($type);
		// status
		$status = $this->_status($type);


		$user = !is_null($user) ? $user : $this->user;

		// check api status for type
		if($user[$status] != 1) return false;

		if(!$user) return false;
		// check status
		if( $user[$status] != 1) {
		    return false;
		}

		// check openid
		$this->openid = get_open_id($user['access_token']);
		if(!$this->openid){
		    return "invalid access_token tid:".$user['tid']."\r\n";
		}

		$sql = "select data from user_data ";
		$sql .= "where tid=".$user['tid'] ;
		$sql .=" and to_char(date,'yyyy-mm-dd') > to_char(to_timestamp(".$user[$mark]."),'yyyy-mm-dd') ";
		$sql .= "order by date asc ";
		$rs = $this->db->getArray($sql);

		// if no $result or count $result less than limit $num, then set status = 0 (no update-data any more); 
		if(!$rs){
			$this->db->update('user_qqhealth_api',array($status=>0),array('tid'=>$user['tid']));
		}

		return $rs;

	}

	private function _redo_upload(){
		echo  "rehandling......\r\n";

		$memcache = get_memcache();
		$key = C('config')['health_handle_error_list'];
		$error_list = unserialize($memcache->get($key));
		# create our client object
		if($error_list && is_array($error_list) ){
			$gmclient= new GearmanClient();
			$gmclient->addServer();
			foreach($error_list as $k=>$v){
				$params = serialize(array('user'=>$v['user'],'data'=>$v['data'],'type'=>$v['type']));
				unset($error_list[$k]);
				$memcache->replace($key,serialize($error_list));
				$gmclient->doBackground("health_bg_upload", $params);	
			}
		}

	}

	private function  _after_upload_handle(){
		
		if($this->error[$this->user['tid']] ){
			echo $this->date. " , upload error! rehandle...\r\n";
			$memcache = get_memcache();
			// error happend
			$key = C('config')['health_handle_error_list'];
			$error_list = unserialize($memcache->get($key));
			if (!$error_list ) $error_list = array();
			$current_key = md5($this->user['tid'] . $this->date . $this->type);
			$error_list[$current_key] = array('user'=>$this->user,'data'=>$this->data,'type'=>$this->type);

			// var_dump($key);
			// var_dump($error_list);

			$memcache->add($key,serialize($error_list),false,0);

			// rehandle faild datas store in memcache
			$this->_redo_upload();

			return false;
		}

		//  upload success
		// delete current data from $this->data
		foreach($this->data as $k=>$v){
			if($v['date'] == $this->date){
				unset($this->data[$k]);
			}
		}

		// update lastupdatemark*
          		$mark = $this->_mark();
          		$rs = $this->db->getRow("select * from user_qqhealth_api where tid=".$this->user['tid']);
          		$updated_time = $this->_make_lastupdate_time($this->date,$this->lastupdate);
          		if($rs->$mark >= $updated_time ) return true;
          		$this->db->update('user_qqhealth_api',array($mark=>$updated_time ),array('tid'=>$this->user['tid']));
          		return true;
	}



	private function _mark($type=null){
		$type = !is_null($type) ? $type : $this->type;
		switch ($type) {
		    case 'steps':
		            return 'lastupdatemark_step';
		        break;
		    case 'sleep':
		            return 'lastupdatemark_sleep';
		        break;
		    
		    default:
		        # code...
		        break;
		}
	}
	private function _status($type = null){
		$type = !is_null($type) ? $type : $this->type;
		switch ($type) {
		    case 'steps':
		            return 'status_step';
		        break;
		    case 'sleep':
		            return 'status_sleep';
		        break;
		    
		    default:
		        # code...
		        break;
		}
	}

	private function _formate_data(){
		$data = $this->data;
		if($data){
			foreach($data as $k=>$v){
				$one = json_decode($v['data']);
				$date = $one->date;
				$data[$k]['date'] = $date;
			}
			$this->data = $data;
		}
		unset($data);
	}

	private function _make_lastupdate_time($date,$lastupdate){
		// return if has a same day
		if(date("Y-m-d",$lastupdate) == $date) return $lastupdate;
		if(date("Y-m-d",$lastupdate) > $date) {
			return strtotime($date . " 23:59:59");
		}
		return strtotime($date);
	}

	// handle step data
	private function _handle_report_steps_by_user($user,$data){
		$this->access_token = $this->user['access_token'];

		$this->appid = C('config')['app_id'];
	         	 // check openid
	               $this->openid = get_open_id($this->access_token);
	               if(!$this->openid){
	               	echo  "invalid access_token";
	          		return false;
	               }

		$data = json_decode($data);
		$this->date = $data->date;
		$this->lastupdate = $data->lastUpdate;
		
		$postArr = array("pf"=>"qzone");
		$postArr['oauth_consumer_key'] = $this->appid;
		$postArr['openid'] = $this->openid;
		$postArr['access_token'] = $this->access_token;
		// update timestamp
		$postArr['time'] = $this->_make_lastupdate_time($data->date,$data->lastUpdate);
		$postArr['distance'] = ($data->walkDistance + $data->runDistance) * 1000;
		$postArr['steps'] = $data->walkSteps + $data->runSteps ;
		$postArr['calories'] = $data->calories;
		$postArr['duration'] = $data->totalActivetime;
		if(isset($data->target)){
			$postArr['target'] = $data->target  ;
			$postArr['achieve'] = (int)$postArr['target'] == 0 ? 0 : ($postArr['steps']/$postArr['target']);
		}


		//  take action!!!!!!!!!!!!!!!!
		$url = C('config')['report_steps_url'];
		$rs = post($url,$postArr);

		// die;

		if(!is_json($rs)){
		      // not a normal return , do not do anything
		// $this->rows_destory[] = $v['id'];
		      $this->error[$user['tid']] = true;
		      return false;
		}

		$result = json_decode($rs);

		// upload success
		if($result->ret == 0){

		      echo  " upload successfully , tid: ".$user['tid'].", type: {$this->type} , date: " . date('Y-m-d H:i:s',$postArr['time']) ."\r\n";

		      usleep(C('config')['qq_upload_sleep_usec']);

		} else{
			$this->error[$user['tid']] = true;
		    	return false;
		}//  handle one row end

		


	}


	private function _handle_report_sleep_by_user($user,$data){
		$this->access_token = $this->user['access_token'];

		$this->appid = C('config')['app_id'];
	         	 // check openid
	               $this->openid = get_open_id($this->access_token);
	               if(!$this->openid){
	               	echo  "invalid access_token";
	          		return false;
	               }

	               $data = json_decode($data);
	               $postArr = array("pf"=>"qzone");
		$postArr['oauth_consumer_key'] = $this->appid;
		$postArr['openid'] = $this->openid;
		$postArr['access_token'] = $this->access_token;

		if( !isset($data->end_time) || !isset($data->start_time)){
			echo "no sleep data upload,tid: ".$user['tid'].", date: ".$data->date."\r\n";
			return;
		}
		$postArr['end_time'] = $data->end_time;
		$postArr['start_time'] = $data->start_time;
		$postArr['total_time'] = $data->total_time;
		$postArr['light_sleep'] = $data->light_sleep;
		$postArr['deep_sleep'] = $data->deep_sleep;
		$postArr['awake_time'] = $data->awake_time;
		$postArr['detail'] = $data->detail;

		//  take action!!!!!!!!!!!!!!!!
		$url = C('config')['report_sleep_url'];
		$rs = post($url,$postArr);

		if(!is_json($rs)){
		      // not a normal return , do not do anything
		// $this->rows_destory[] = $v['id'];
		      $this->error[$user['tid']] = true;
		      return false;
		}

		$result = json_decode($rs);

		// upload success
		if($result->ret == 0){

		      echo  " upload successfully , tid: ".$user['tid'].", type: {$this->type} , date: " . $data->date ."\r\n";

		      usleep(C('config')['qq_upload_sleep_usec']);

		} else{
			$this->error[$user['tid']] = true;
		    	return false;
		}//  handle one row end

		
	}

}


/***************************************************************************************/
//					data handle
/***************************************************************************************/

// data handle class
class Data_handle{
	public $current_file;
	public $tid;
	public $token;
	public $qqupload_update_users;
	public $error;

	private $data_path;
	private $db;
	public function __construct(){
		require_once FCPATH . '/system/helpers/file_helper.php';
		$this->data_path = C('config')['upload_path'];
		$this->db = db_instanse();
	}


	public function run($info ){
		// handle automatically 
		if($info == 'auto'){

			$limit = C('config')['file_handle_limit'];
			$fArr = get_dir_file_info($this->data_path);
			$count = 0;
			if($fArr){
				foreach ($fArr as $v) {
					if($count >= $limit){
						break;
					}
					if(is_file($v['server_path'])){
						$this->current_file = $v['server_path'];
						$this->_fileHandle($v['name']);
					  	$count++;	
					}
					  
				}
			}
		}else{ // handle the file
			$file = $info['full_path'];
			if(!is_file($file)) {
				echo 'invalid data';
				return;
			}
			$this->current_file = $file;
			$this->_fileHandle($info['orig_name']);
		}

	}



	private function _fileHandle($orig_name){
		// var_dump($this->current_file);die;
		$name =  substr($orig_name,0,strpos($orig_name, strrchr($orig_name, '.')) );
		$arr = explode('@##@', trim($name));
		// get user_id from filename
		if(count($arr) == 2){
			$this->tid   		= $arr[0];
			$this->token 		= $arr[1];
		}else{
			// wrong filename
			// var_dump('wrong file');
			$this->_rmFile();
			echo 'invalid filename:' . $this->current_file;
			// print error
			return false;
		}

		// check if it is a valid user
		if(!$this->_checkUserId()){
			echo 'unknow user id';
			return false;
		}

		
		$this->_doHandle();
	}

	private function _doHandle(){
		// get extension
		$extension = strtolower(substr(strrchr($this->current_file, '.'), 1));
		// zip uncompress
		if(preg_match('/zip$/i',$extension)){

			// read zip file , return array data
			$data = read_zip($this->current_file);

			if(!empty($data) && is_array($data)){
				// user infos data
				$infos = array();
				foreach ($data as $k => $v) {
					
					if(!is_file($v)) {
						echo 'not a file';
						continue;
					}

					// image upload to S3 
					
					if(getimagesize($v)){
						// is upload to S3
						if( C('config')['is_upload_s3'] ){
							$bucket = C('config')['s3_bucket'];
							$client = get_s3client();
							if(!$client ) {
								echo "falid to upload image\n";
								continue;	
							}
							$info = pathinfo($v);
							$name = substr(md5($this->token), 0 , 16). "." . $info['extension'];

							try{
								$result = $client->putObject(array(
									'ACL'=> 'public-read',
								    'Bucket'     => $bucket,
								    'Key'        => $name,
								    'SourceFile' => $v,
								));
								// var_dump($result);
							} catch (Exception $e){
								// echo 'Message: ' .$e->getMessage();
								continue;
							}


							$infos['pic'] = $name;
						}

						
						if(is_file($v)) unlink($v);


					}


					// .json file
					if(preg_match('/.*\.json$/is', $v)){

						$str = file_get_contents($v);

						if(is_file($v)) unlink($v);

						if(!is_json($str)) continue;
						$json = json_decode($str);
						//*1 save sport data
						$this->_saveUserData($json->data);

						if(isset($json->data)) {
							unset($json->data);
						}
						
						$info = array();
						if(!empty($json)){
							foreach ($json as $k => $v) {
								if($k == 'userinfo'){
									$info['lastupdatemark'] = isset($v->lastupdatemark) ? ($v->lastupdatemark) : 0;
								}
								if(is_object($v) || is_array($v)){
									$info[$k] = json_encode($v);
								}else{
									$info[$k] = $v;
								}

							}
						}
						// var_dump($info);
						$infos = array_merge($infos,$info);
					}


				}

				//*2 save user infos
				$this->_saveUserInfo($infos);

			}


		} // handle one file end

		// QQ health upload
		gearman_client(serialize(array('token'=>$this->token)));

		if(!empty($this->qqupload_update_users)){
			
			foreach($this->qqupload_update_users as $k=>$v){
				$this->qqupload_update_user_set($k);
			}
			
		}

		// judge if delete file
		if(!empty($this->error) && is_array($this->error)){
			foreach ($error as $k => $v) {
				if($this->current_file == $k){
					continue;
				}
				$this->_rmFile();
			}
		}else{
			$this->_rmFile();
		}


	}
	/**	
	 * [_saveUserInfo description]
	 * @param  [type] $json [description]
	 * @return [void]       [description]
	 */
	private function _saveUserInfo($data){
		$data['tid'] = $this->tid;
	
		if(count($data) < 2){
			return;
		}
		// $userinfo = json_encode($jsonArr['userinfo']);

		$table = 'user_infos';
		$where = array('tid'=>$this->tid);
		$result = $this->db->getRow("select tid from ".$table." where tid=".$this->tid);
		if($result){
			// update
			if(!$this->db->update($table,$data,$where)){
				// update error
				$this->error();
			}
		}else{
			// insert
			if($this->db->insert($table,$data)){
				return true;
			}else{
				$this->error();
			}
		}
		

	}

	/**
	 * [_saveUserData description]
	 * @param  [type] $data [description]
	 * @return [type]       [description]
	 */
	private function _saveUserData($data){
		$data = jsonObj_to_array($data);
		foreach ($data as $k => $v) {
			// save one day
			$this->_save($v);
		}


	}

	private function _save($data){

		$table = 'user_data';
		$date = $data['date'];
		$lastupdate = $data['lastUpdate'];

		$where = array('tid'=>$this->tid,'date'=>$date);

		$result = $this->db->getRow("select * from user_data where tid=".$this->tid." and date='{$date}'");

		if($result){// update
			
			// var_dump($lastupdate)	;
			// var_dump($result->lastupdate);
			if( $lastupdate > $result->lastupdate){
				// update
				$array = array(
					'tid'			=>	$this->tid,
					'date'			=>	$date,
					'lastupdate' 	=>  $lastupdate,
					'data'			=>	json_encode($data),
				);
				if($this->db->update('user_data',$array,$where)){
					// mark user data has updated
					$this->qqupload_update_users[$this->tid] = true;
					return true;
				}

				// update error
				$this->error();
				
			}else{
				// older data , ignore
			}
		}else{// insert
			
			$array = array(
				'tid'			=>	$this->tid,
				'date'			=>	$date,
				'lastupdate' 	=>  $lastupdate,
				'data'			=>	json_encode($data),
			);

			// var_dump($array);
			if($this->db->insert($table,$array)){
				// mark user data has updated
				$this->qqupload_update_users[$this->tid] = true;

				return true;
			}else{
				$this->error();
			}
		}
	}

	/**
	 * error return
	 * @return [bool] [description]
	 */
	public function error(){
		$this->error[$this->current_file] = true;
		return false;
	}

	/**
	 * [turn on user qq upload ]
	 * @return [type] [description]
	 */
	private function qqupload_update_user_set($tid){
		if(!$tid) return;
		$table = 'user_qqhealth_api';
		$result = $this->db->getRow("select * from {$table} where tid={$tid} and is_join=1");
		$where = array('tid'=>$tid);
		if($result && $result->status_step!=1){
			$data = array("status_step"=>1);
			if(isset($result->status_sleep) && $result->status_sleep != 1){
				$data['status_sleep'] = 1;
			}
			$this->db->update($table,$data,$where);
		}
	}

	/**
	 * delete the current file
	 * @return [type] [description]
	 */
	private function _rmFile(){
		if(is_file($this->current_file)){
			unlink($this->current_file);
			$this->current_file =  null;
		}
	}

	/**
	 * [_checkUserId check if it is a valid user]
	 * @return [type] [description]
	 */
	private function _checkUserId(){
		$where = array('tid'=>$this->tid,'token'=>$this->token);
		$result = $this->db->getRow("select id from user_token where tid=".$this->tid." and token='{$this->token}'");
		if(!$result){
			$this->_rmFile();
			return false;
		}
		return true;
	}


}